package net.apexes.commons.ormlite;

import com.j256.ormlite.db.DatabaseType;
import com.j256.ormlite.field.DatabaseFieldConfig;
import com.j256.ormlite.field.FieldType;
import com.j256.ormlite.support.ConnectionSource;
import com.j256.ormlite.table.DatabaseTableConfig;

import java.lang.reflect.Field;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author <a href=mailto:hedyn@foxmail.com>HeDYn</a>
 */
class UnreflectDatabaseTableConfig<T> extends DatabaseTableConfig<T> {

    private final Table<T> table;
    private final Class<T> dataClass;
    private final String tableName;
    private final List<DatabaseFieldConfig> fieldConfigs;
    private FieldType[] fieldTypes;

    public UnreflectDatabaseTableConfig(Table<T> table, Class<T> dataClass, String tableName, List<DatabaseFieldConfig> fieldConfigs) {
        super(dataClass, tableName, fieldConfigs);
        this.table = table;
        this.dataClass = dataClass;
        this.tableName = tableName;
        this.fieldConfigs = fieldConfigs;
    }

    @Override
    public FieldType[] getFieldTypes(DatabaseType databaseType) throws SQLException {
        if (fieldTypes == null) {
            fieldTypes = super.getFieldTypes(databaseType);
        }
        return fieldTypes;
    }

    @Override
    public void extractFieldTypes(ConnectionSource connectionSource) throws SQLException {
        if (fieldTypes == null) {
            fieldTypes = convertFieldConfigs(connectionSource, tableName, fieldConfigs);
        }
    }

    private FieldType[] convertFieldConfigs(ConnectionSource connectionSource, String tableName, List<DatabaseFieldConfig> fieldConfigs) throws SQLException {
        List<FieldType> fieldTypes = new ArrayList<>();
        for (DatabaseFieldConfig fieldConfig : fieldConfigs) {
            FieldType fieldType = null;
            // walk up the classes until we find the field
            for (Class<?> classWalk = dataClass; classWalk != null; classWalk = classWalk.getSuperclass()) {
                Field field;
                try {
                    field = classWalk.getDeclaredField(fieldConfig.getFieldName());
                } catch (NoSuchFieldException e) {
                    // we ignore this and just loop hopefully finding it in a upper class
                    continue;
                }
                if (field != null) {
                    fieldType = new UnreflectFieldType<>(connectionSource, table, tableName, field, fieldConfig, dataClass);
                    break;
                }
            }

            if (fieldType == null) {
                throw new SQLException("Could not find declared field with name '" + fieldConfig.getFieldName()
                        + "' for " + dataClass);
            }
            fieldTypes.add(fieldType);
        }
        if (fieldTypes.isEmpty()) {
            throw new SQLException("No fields were configured for class " + dataClass);
        }
        return fieldTypes.toArray(new FieldType[fieldTypes.size()]);
    }
}
